package own.stu.inter.loadbalance.ping;

import own.stu.inter.loadbalance.LoadBalancer;
import own.stu.inter.loadbalance.server.ServerInstance;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;

public class PingService {

    // TODO 线程池关闭 触发
    private ScheduledThreadPoolExecutor pingScheduledExecutor = new ScheduledThreadPoolExecutor(1);

    private AtomicBoolean pingInProgress = new AtomicBoolean(false);
    private int intervalSeconds = 10;

    private LoadBalancer loadBalancer;

    private IPing ping;
    private IPingStrategy pingStrategy;

    public PingService(LoadBalancer loadBalancer, IPing ping, IPingStrategy pingStrategy) {
        this.loadBalancer = loadBalancer;
        this.ping = ping;
        this.pingStrategy = pingStrategy;
    }

    public void init() {
        pingScheduledExecutor.schedule(() -> sendPing(), intervalSeconds, TimeUnit.SECONDS);
    }

    public void sendPing() {
        if (!pingInProgress.compareAndSet(false, true)) {
            return;
        }

        try {
            List<ServerInstance> allServerList = loadBalancer.getAllServers();
            ReadWriteLock allServerLock = loadBalancer.getAllServerLock();
            // TODO del ?
            List<ServerInstance> activeServers = loadBalancer.getActiveServers();
            ReadWriteLock activeServerLock = loadBalancer.getActiveServerLock();

            Lock rLock = allServerLock.readLock();

            rLock.lock();
            List<ServerInstance> allServers = new ArrayList<>(allServerList);
            rLock.unlock();

            List<Boolean> pingResults = pingStrategy.pingServers(ping, allServers);
            List<ServerInstance> newActiveService = new ArrayList<>();

            boolean changed = false;
            for (int i = 0; i < pingResults.size(); i++) {
                final boolean pResult = pingResults.get(i) != null && pingResults.get(i);
                final ServerInstance instance = allServers.get(i);
                if (pResult != instance.isAlive()) {
                    changed = true;
                }

                if (pResult) {
                    newActiveService.add(instance);
                }
            }

            if (changed) {
                loadBalancer.setActiveServerList(newActiveService);
            }

        } finally {
            pingInProgress.set(false);
        }
    }
}
